module axi_slave (
    input clk,    // Clock
    input rst,  // Asynchronous reset active low
            //read addr
    output        o_arready,
    input         i_arvalid,
    input  [31:0] i_araddr,
    input  [3:0]  i_arid,
    input  [7:0]  i_arlen,
    input  [2:0]  i_arsize,
    input  [1:0]  i_arburst,
        //read data
    input         i_rready,
    output        o_rvalid,
    output [1:0]  o_rresp,
    output [63:0] o_rdata,
    output        o_rlast,
    output [3:0]  o_rid,
        //write addr
    output        o_awready,
    input         i_awvalid,
    input  [31:0] i_awaddr,
    input  [3:0]  i_awid,
    input  [7:0]  i_awlen,
    input  [2:0]  i_awsize,
    input  [1:0]  i_awburst,
        //write data
    output        o_wready,
    input         i_wvalid,
    input  [63:0] i_wdata,
    input  [7:0]  i_wstrb,
    input         i_wlast,
        //write res
    input         i_bready,
    output        o_bvalid,
    output [1:0]  o_bresp,
    output [3:0]  o_bid
    
);

reg [3:0]rstate,rnstate;
reg [3:0]wstate,wnstate;
reg [7:0] rdcnt,rdcnt_max;
reg [7:0] wrcnt,wrcnt_max;
reg [7:0] rmask,wmask;
reg [4:0] raddr_add;
reg [4:0] waddr_add;
reg [31:0] raddr;
reg       arready;
reg       rvalid;
reg [1:0] rresp;
reg [63:0]rdata;
reg       rlast;
reg [3:0] rid;
reg [63:0] dpi_rdata;
import "DPI-C" function void pmem_read(
    input int raddr, output longint rdata,input byte rmask);
parameter RIDLE=0,AREAD=1,READ_DATA=2,READ_SEND=3,READ_FIN=4;
always @(posedge clk ) begin
    if(rst) begin
        rstate <= 0;
    end else begin
        rstate <= rnstate ;
    end
end

always @(*) begin
    case (rstate)
        RIDLE:begin 
            if (i_arvalid && o_arready) begin
                rnstate = AREAD;
            end
        end
        AREAD:begin 
            if (i_rready) begin
                rnstate = READ_DATA;
            end
        end
        READ_DATA:begin 
            if ( rdcnt == rdcnt_max -1) begin
                rnstate = READ_FIN;
            end
            else  begin 
                rnstate = READ_SEND;
            end
        end
        READ_SEND:begin 
            rnstate = READ_DATA;
        end
        READ_FIN:begin 
            rnstate = RIDLE;
        end
        default : /* default */;
    endcase
end

always @(posedge clk or negedge rst) begin
    if(rst) begin
        rvalid      <= 'b0;
        rresp       <= 'b0;
        rid         <= 'b0;
        rlast       <= 'b0;
        rdcnt_max   <= 'b0;
        raddr_add   <= 'b0;
        raddr       <= 'b0;
        arready     <= 1'b1;
        rmask       <= 'b0;
        rdcnt       <= 'b0;
    end else begin
        case (rnstate)
            RIDLE:begin 
                rvalid      <= 'b0;
                rresp       <= 'b0;
                rid         <= 'b0;
                rlast       <= 'b0;
                rdcnt_max   <= 'b0;
                raddr_add   <= 'b0;
                raddr       <= 'b0;
                arready     <= 1'b1;
                rmask       <= 'b0;
                rdcnt       <= 'b0;
            end
            AREAD:begin 
                rdcnt_max <= 8'h1<<i_arlen;
                case (i_arsize)
                    3'b000:raddr_add <= 5'h1; //8
                    3'b001:raddr_add <= 5'h2; //16
                    3'b010:raddr_add <= 5'h4; //32
                    3'b011:raddr_add <= 5'h8; //64
                    default: ;
                endcase
                raddr     <= i_araddr;
                case (i_arsize)
                    3'b000:rmask <= 8'd1;
                    3'b001:rmask <= 8'd3;
                    3'b010:rmask <= 8'd7;
                    3'b011:rmask <= 8'hf;
                    3'b100:rmask <= 8'd128;
                    default : /* default */;
                endcase
            end
            READ_DATA:begin 
                pmem_read(raddr+raddr_add*rdcnt,dpi_rdata,rmask);
                rvalid <= 1'b0;
            end
            READ_SEND:begin 
                rvalid <= 1'b1;
                rresp  <= 2'b0;
                rid    <= 4'b0;
                rlast  <= 1'b0;
                rdcnt  <= rdcnt + 1;
                rdata  <= dpi_rdata;
            end
            READ_FIN:begin 
                rvalid <= 1'b1;
                rresp  <= 2'b0;
                rid    <= 4'b0;
                rlast  <= 1'b1;
                rdata  <= dpi_rdata;
            end
            default : /* default */;
        endcase
    end
end


reg [31:0] waddr;
reg [63:0] wdata;
reg        awready;
reg        wready;
reg        bvalid;
reg [1:0]  bresp;
reg [3:0]  bid;
reg        wlast;
reg [7:0]  wstrb;
always @(posedge clk or negedge rst) begin 
    if(rst) begin
        wstate <= 0;
    end else begin
        wstate <= wnstate;
    end
end
parameter WIDLE=0,AWRITE=1,WRITE_DATA=2,WRITE_REC=3,RESB=4;
always @(*) begin 
    case (wstate)
        WIDLE:begin 
            if (i_awvalid && o_awready) begin
                wnstate = AWRITE;
            end
        end
        AWRITE:begin 
            if (i_wvalid&&o_wready) begin
                wnstate = WRITE_REC;
            end
        end
        WRITE_REC:begin 
            wnstate = WRITE_DATA;
        end
        WRITE_DATA:begin 
            if (wrcnt == wrcnt_max) begin
                wnstate = RESB;
            end
            else if(i_wvalid&&o_wready) begin 
                wnstate = WRITE_REC;
            end
        end
        RESB:begin 
            wnstate = WIDLE;
        end
        default : /* default */;
    endcase
end
import "DPI-C" function void pmem_write(
    input int waddr, input longint wdata, input byte wmask);
always @(posedge clk or negedge rst) begin 
    if(rst) begin
        wrcnt_max <= 'b0;
        waddr_add <= 'b0;
        waddr     <= 'b0;
        wmask     <= 'b0;
        wlast     <= 'b0;
        wdata     <= 'b0;
        wstrb     <= 'b0;
        bvalid    <= 'b0;
        bresp     <= 'b0;
        bid       <= 'b0;
        wready    <= 1'b1;
        awready   <= 1'b1;
        wrcnt     <= 'b0;
    end else begin
        case (wnstate)
            WIDLE:begin 
                wrcnt_max <= 'b0;
                waddr_add <= 'b0;
                waddr     <= 'b0;
                wmask     <= 'b0;
                wlast     <= 'b0;
                wdata     <= 'b0;
                wstrb     <= 'b0;
                bvalid    <= 'b0;
                bresp     <= 'b0;
                bid       <= 'b0;
                wready    <= 1'b1;
                awready   <= 1'b1;
                wrcnt     <= 'b0;
            end
            AWRITE:begin 
                wrcnt_max <= 8'b1  << i_awlen;
                case (i_awsize)
                    3'b000:waddr_add <= 5'h1; //8
                    3'b001:waddr_add <= 5'h2; //16
                    3'b010:waddr_add <= 5'h4; //32
                    3'b011:waddr_add <= 5'h8; //64
                    default: ;
                endcase
                waddr     <= i_awaddr;
                 case (i_awsize)
                    3'b000:wmask <= 8'd1;
                    3'b001:wmask <= 8'd3;
                    3'b010:wmask <= 8'd7;
                    3'b011:wmask <= 8'hf;
                    3'b100:wmask <= 8'd128;
                    default : /* default */;
                endcase
            end
            WRITE_REC:begin 
                wlast <= i_wlast;
                wdata <= i_wdata;
                wstrb <= i_wstrb;
                wready <= 1'b0;
            end
            WRITE_DATA:begin 
                 pmem_write(waddr+waddr_add*wrcnt,wdata,wmask);
                 wready <= 1'b1;
                 wrcnt <= wrcnt + 1;
            end
            RESB:begin 
                bvalid <= 1'b1;
                bresp  <= 'b0;
                bid    <= 'b0;
            end
            default : /* default */;
        endcase
    end
end

assign   o_arready  = arready;
assign   o_rvalid   = rvalid;
assign   o_rresp    = rresp;
assign   o_rdata    = rdata;
assign   o_rlast    = rlast;
assign   o_rid      = rid;
assign   o_awready  = awready; 
assign   o_wready   = wready;
assign   o_bvalid   = bvalid;
assign   o_bresp    = bresp;
assign   o_bid      = bid;





endmodule